package org.wabson.alfresco.translations;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Hashtable;
import java.util.Set;
import java.util.TreeSet;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/**
 * Abstract class for loading message bundle files from a ZIP/JAR file.
 * 
 * Based on code from http://www.javaworld.com/javaworld/javatips/jw-javatip49.html
 * for reading JAR files using java.util.zip.ZipInputStream
 * 
 * @author wabson
 */
public abstract class AbstractJarLoader
{
	public static final String[] DEFAULT_IGNORE_PATHS = { "META-INF" };
	
    /**
     * Base directory path within the JAR
     */
    protected String dirPath;
    
    /**
     * Prefixes of any paths which should not be loaded
     */
    protected String[] ignorePaths;

	protected Hashtable<String, byte[]> htJarContents=new Hashtable<String, byte[]>();

    private boolean debugOn=false;
    
    /**
     * Initialise the loader, reading in the contents of the specified InputStream
     * which is assumed to be that of a valid JAR file.
     * 
     * @param jis	InputStream of the JAR or ZIP file to be read
     */
    protected void init(InputStream jis)
    {
    	ignorePaths = DEFAULT_IGNORE_PATHS;
        ZipInputStream zis=new ZipInputStream(new BufferedInputStream(jis));
        ZipEntry ze;
        int BUFFER = 2048;
        
        try
        {
            BufferedOutputStream dest;
            while ((ze = zis.getNextEntry()) != null)
            {
                if (ze.isDirectory())
                {
                    continue;
                }
                int count;
                byte data[] = new byte[BUFFER];
                ByteArrayOutputStream fos = new ByteArrayOutputStream();
                dest = new BufferedOutputStream(fos, BUFFER);
                while ((count = zis.read(data, 0, BUFFER)) != -1)
                {
                    dest.write(data, 0, count);
                }
                dest.flush();
                dest.close();

                byte[] b = fos.toByteArray();
                int size = (int) ze.getSize();
                String path = ze.getName();

                if (debugOn)
                {
                    System.err.println(
                       path + 
                       ",size=" + size + "/" + b.length +
                       ",csize=" + ze.getCompressedSize()
                       );
                }
                
                
                // add to internal resource hashtables
                if (dirPath.length() == 0 || ze.getName().startsWith(dirPath))
                {
                    boolean ignore = false;
                    for (String ignorePath : ignorePaths)
                    {
    					ignore = ignore || ze.getName().startsWith(ignorePath);
    				}
                    
                    if (!ignore)
                    {
	                    path = path.substring(dirPath.length());
	                    if (path.startsWith("/"))
	                    {
	                        path = path.substring(1);
	                    }
	                    if (debugOn)
	                    {
	                        System.err.println("PUT " + path + " - " + b.length + " bytes");
	                    }
	                    htJarContents.put(path, b);
                    }
                }
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        finally
        {
            try
            {
                zis.close();
                jis.close();
            }
            catch (IOException e)
            {
            }
        }
    }

    /* (non-Javadoc)
     * @see org.wabson.alfresco.translations.BundleLoader#exists(java.lang.String)
     */
    public boolean exists(String filePath)
    {
        return htJarContents.containsKey(filePath);
    }

    /* (non-Javadoc)
     * @see org.wabson.alfresco.translations.BundleLoader#load(java.lang.String)
     */
    public MessageBundle load(String filePath) throws BundleLoadingException
    {
        MessageBundle mb = new MessageBundle(filePath);
        byte[] b = htJarContents.get(filePath);
        if (b != null)
        {
            if (debugOn)
                System.err.println("GET " + filePath + " - " + (b == null ? "null" : b.length) + " bytes");
            BufferedReader in = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(b)));
    
            String s;
            try
            {
                while ((s = in.readLine()) != null)
                {
                    mb.addLine(s);
                }
            }
            catch (IOException e)
            {
                throw new BundleLoadingException(e);
            }
            finally
            {
                try
                {
                    in.close();
                }
                catch (IOException e)
                {
                }
            }
        }
        else
        {
            throw new BundleLoadingException("Could not find file " + filePath + " within the bundle");
        }
        return mb;
    }

    /* (non-Javadoc)
     * @see org.wabson.alfresco.translations.BundleLoader#findBundleFiles()
     */
    public Set<String> findBundleFiles()
    {
        Set<String> bundles = new TreeSet<String>();
        bundles.addAll(htJarContents.keySet());
        return bundles;
    }

    public String getDirPath()
    {
        return dirPath;
    }

    public void setDirPath(String dirPath)
    {
        this.dirPath = dirPath;
    }
    
    /**
     * Return the number of files in the package, such as properties and text 
     * files
     * 
     * @return  The total number of files
     */
    public int getNumFiles()
    {
        return htJarContents.size();
    }
    
    /**
     * Get the list of all files contained in the package
     * 
     * @return  The set of file names in alphabetical order
     */
    public Set<String> getFiles()
    {
        return new TreeSet<String>(htJarContents.keySet());
    }
    
    /**
     * Get the list of all bundle files contained in the package
     * 
     * @return  The set of file names in alphabetical order
     */
    public Set<String> getBundleFiles()
    {
        TreeSet<String> s = new TreeSet<String>();
        for (String fileName : htJarContents.keySet())
        {
            if (fileName.endsWith(".properties"))
                s.add(fileName);
        }
        return s;
    }
    
    /**
     * Get the list of path prefixes to ignore when loading file contents
     * 
     * @return	Array containing all the prefixes which if matched will cause
     * files not to be loaded
     */
    public String[] getIgnorePaths() {
		return ignorePaths;
	}

    /**
     * Set the list of ignore paths
     * 
     * @param ignorePaths	Array containing all the prefixes which if matched will cause
     * files not to be loaded
     */
	public void setIgnorePaths(String[] ignorePaths) {
		this.ignorePaths = ignorePaths;
	}

}
